﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidgren.Network;
using System.Threading;
using System.Net;
#endregion

namespace BreakOutBattles.IO.Net
{
    public class GameServer
    {
        #region Using Statements
        private const string SERVER_NAME = "BreakoutBattlesServer";
        private const int DEFAULT_MAX_CONNECTIONS = 2;
        private const int DEFAULT_PORT = 8989;

        private NetPeerConfiguration serverConfig;
        private NetServer server;
        private NetConnection host;
        #endregion

        /// <summary>
        /// Creates a new game server with default configuration.
        /// </summary>
        public GameServer()
        {
            serverConfig = new NetPeerConfiguration(SERVER_NAME);
            serverConfig.Port = DEFAULT_PORT;
            serverConfig.EnableUPnP = true;
            serverConfig.UseMessageRecycling = true;
            serverConfig.MaximumConnections = DEFAULT_MAX_CONNECTIONS;
            serverConfig.EnableMessageType(NetIncomingMessageType.DiscoveryRequest);

            server = new NetServer(serverConfig);
            server.RegisterReceivedCallback(new SendOrPostCallback(ServerMessageHandler));
        }

        /// <summary>
        /// Starts the game server, allowing it to send and receive messages.
        /// </summary>
        public void Start()
        {
            server.Start();
            Console.WriteLine("Server started!!");
        }

        /// <summary>
        /// Attempts to stop the server.
        /// </summary>
        public void Stop()
        {
            server.Shutdown("Closed");
        }

        /// <summary>
        /// Creates an outgoing message that contains the same data as an
        /// incoming message.
        /// </summary>
        /// <param name="incomingMessage">The incoming message that contains the data to read.</param>
        /// <returns>An outcoming message.</returns>
        private NetOutgoingMessage CreateGameDataMessage(NetIncomingMessage incomingMessage)
        {
            NetOutgoingMessage message = server.CreateMessage();

            return message;
        }

        private void ServerMessageHandler(object peer)
        {
            NetIncomingMessage message;

            while ((message = server.ReadMessage()) != null)
            {
                switch (message.MessageType)
                {
                    case NetIncomingMessageType.StatusChanged:
                        NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();

                        if (status == NetConnectionStatus.Connected)
                        {
                            Console.WriteLine(NetUtility.ToHexString(message.SenderConnection.RemoteUniqueIdentifier) + " connected!");

                            if (host == null)
                            {
                                host = server.Connections[0];
                            }
                        }
                        else if (status == NetConnectionStatus.Disconnected)
                        {
                            if (server.Connections.Count == 0)
                            {
                                host = null;
                            }
                        }
                        break;
                   
                    case NetIncomingMessageType.Data:
                        /*
                         * Resend the received game data to the other clients.
                         * Dont need to care about what type of data the server
                         * received.
                         */
                        NetOutgoingMessage msgTosend = CreateGameDataMessage(message);
                        server.SendToAll(msgTosend, message.SenderConnection, NetDeliveryMethod.ReliableOrdered, 0);
                        break;

                    case NetIncomingMessageType.DiscoveryRequest:
                        server.SendDiscoveryResponse(null, message.SenderEndpoint);
                        break;

                    case NetIncomingMessageType.DebugMessage:
                    case NetIncomingMessageType.ErrorMessage:
                    case NetIncomingMessageType.WarningMessage:
                    case NetIncomingMessageType.VerboseDebugMessage:
                        Console.WriteLine(message.ReadString());
                        break;
                }
            }
        }
    }
}
